{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用元类\n",
    "\n",
    "## 使用type()定义实例\n",
    "\n",
    "动态语言和静态语言最大的不同，就是函数和类的定义，不是编译时定义的，而是运行时动态创建的。\n",
    "\n",
    "`type()`函数可以用来检测对象的类型:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "type(Hello):  <class 'type'>\n",
      "type(h):  <class '__main__.Hello'>\n"
     ]
    }
   ],
   "source": [
    "class Hello(object):\n",
    "    def hello(self, name='world'):\n",
    "        print('Hello, %s.' % name)\n",
    "        \n",
    "h = Hello()\n",
    "print(\"type(Hello): \", type(Hello))\n",
    "print(\"type(h): \", type(h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述可知, `Hello`的类型是`tyep`;`h`的类型是`class`;\n",
    "\n",
    "`type()`函数既可以返回一个对象的类型，又可以创建出新的类型，比如，我们可以通过`type()`函数创建出`Hello`类，而无需通过`class Hello(object)...`的定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, world.\n",
      "h:  None\n",
      "type(Hello):  <class 'type'>\n",
      "type(h):  <class '__main__.Hello'>\n"
     ]
    }
   ],
   "source": [
    "def fn(self, name='world'): # 先定义函数\n",
    "    print('Hello, %s.' % name)\n",
    "\n",
    "# 格式type(类名, 父类，接口)\n",
    "Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class\n",
    "\n",
    "h = Hello() # 定义类的实例\n",
    "print(\"h: \", h.hello())\n",
    "print(\"type(Hello): \", type(Hello))\n",
    "print(\"type(h): \", type(h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要创建一个`class`对象，`type()`函数依次传入3个参数：\n",
    "\n",
    "1. class的名称；\n",
    "2. 继承的父类集合，注意Python支持多重继承，如果只有一个父类，别忘了tuple的单元素写法；\n",
    "3. class的方法名称与函数绑定，这里我们把函数fn绑定到方法名hello上。\n",
    "\n",
    "通过type()函数创建的类和直接写class是完全一样的，因为Python解释器遇到class定义时，仅仅是扫描一下class定义的语法，然后调用type()函数创建出class。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用metaclass创建class\n",
    "\n",
    "除了使用`type()`动态创建类以外，要控制类的创建行为，还可以使用`metaclass`。`metaclass`，直译为元类，简单的解释就是：当我们定义了类以后，就可以根据这个类创建出实例，所以：先定义类，然后创建实例。但是如果我们想创建出类呢？那就必须根据`metaclass`创建出类，所以：先定义`metaclass`，然后创建类。\n",
    "\n",
    "连接起来就是：先定义`metaclass`，就可以创建类，最后创建实例。\n",
    "\n",
    "所以，`metaclass`允许你创建类或者修改类。换句话说，你可以把类看成是`metaclass`创建出来的“实例”。\n",
    "\n",
    "我们先看一个简单的例子，这个`metaclass`可以给我们自定义的`MyList`增加一个`add`方法：\n",
    "\n",
    "定义`ListMetaclass`，按照默认习惯，`metaclass`的类名总是以`Metaclass`结尾，以便清楚地表示这是一个`metaclass`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "L:  [1]\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'list' object has no attribute 'add'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-1-d1b95795a1c2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     18\u001b[0m \u001b[1;31m# 而普通的list没有add()方法：\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     19\u001b[0m \u001b[0mL2\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 20\u001b[1;33m \u001b[0mL2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'list' object has no attribute 'add'"
     ]
    }
   ],
   "source": [
    "# metaclass是类的模板，所以必须从`type`类型派生：\n",
    "class ListMetaclass(type):\n",
    "    def __new__(cls, name, bases, attrs):\n",
    "        # 添加接口函数“add”\n",
    "        attrs['add'] = lambda self, value: self.append(value)\n",
    "        \n",
    "        return type.__new__(cls, name, bases, attrs)\n",
    "    \n",
    "# 有了ListMetaclass，我们在定义类的时候还要指示使用ListMetaclass来定制类，\n",
    "# 传入关键字参数metaclass\n",
    "class MyList(list, metaclass=ListMetaclass):\n",
    "    pass\n",
    "\n",
    "L = MyList()\n",
    "L.add(1)\n",
    "print(\"L: \", L)\n",
    "\n",
    "# 而普通的list没有add()方法：\n",
    "L2 = list()\n",
    "L2.add(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们传入关键字参数`metaclass`时，魔术就生效了，它指示`Python`解释器在创建`MyList`时，要通过`ListMetaclass.__new__()`来创建，在此，我们可以修改类的定义，比如，加上新的方法，然后，返回修改后的定义。\n",
    "\n",
    "`__new__()`方法接收到的参数依次是：\n",
    "\n",
    "1. 当前准备创建的类的对象；\n",
    "2. 类的名字；\n",
    "3. 类继承的父类集合；\n",
    "4. 类的方法集合。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
